import Cesium from '../Ces/Cesium'
import WeCustomTextureUniform from './WeCustomTextureUniform'
import WeDataProviderRequest from './WeDataProviderRequest'
import WeImageryLayer from './WeImageryLayer'

class WeDataProvider {
  _onDataUpdate
  private _dataRequest: WeDataProviderRequest
  private dirty: boolean = false
  _imageryLayer: WeImageryLayer
  externalData: any
  constructor(options) {
    this._dataRequest = new WeDataProviderRequest(options)
    this._onDataUpdate = options.onDataUpdate
    this.dirty = true
  }

  get dataRequest() {
    return this._dataRequest
  }

  get url() {
    return this.dataRequest.requestData[0].url
  }
  set url(value) {
    if (value && this.dataRequest.requestData[0].url !== value) {
      this.dataRequest.requestData[0].url = value
      this.dirty = true
    }
  }

  get timeSampleArray() {
    return this.dataRequest.timeSampleArray
  }

  setupDisplay(image, context) {
    const imageryLayer = this._imageryLayer
    const display = imageryLayer.display
    const { externalData, source, valueAndColorRamp, fragmentShaderSource } = image
    this.externalData = externalData || {}
    if (source) {
      display.valueDecoder.isSampler3D = source.depth > 1
      if (valueAndColorRamp) {
        display.valueAndColorRamp = valueAndColorRamp
      }
      display.weDisplayLegend.build(context)
      display.weDisplayLegend.dirty = false
      if (fragmentShaderSource) {
        display.customFragmentShaderSrouce = fragmentShaderSource
      }
    }
    display.generateDecodeFunc()
  }

  setupTilingScheme(image) {
    const imageryLayer = this._imageryLayer
    const display = imageryLayer.display

    let rectangle

    const { west, south, east, north } = image
    const { width, height } = image.source ?? image

    if (Cesium.defined(west)) {
      const dx = (east - west) / (width - 1)
      const dy = (north - south) / (height - 1)
      let fixWest = west - dx * 0.5
      if (fixWest < -180) fixWest = -180
      let fixSouth = south - dy * 0.5
      if (fixSouth < -90) fixSouth = -90
      let fixEast = east + dx * 0.5
      if (fixEast > 180) {
        display.weUniform.Properties.paddingEast = 0.5 / (width - 1)
        fixEast = 180
      }
      let fixNorth = north + dy * 0.5
      if (fixNorth > 90) fixNorth = 90
      rectangle = Cesium.Rectangle.fromDegrees(fixWest, fixSouth, fixEast, fixNorth, rectangle)
    } else {
      rectangle = Cesium.Rectangle.MAX_VALUE
    }

    display.weUniform.Properties.extent = rectangle

    imageryLayer._rectangle = rectangle

    const imageryProvider = this._imageryLayer.imageryProvider

    if (
      imageryProvider.minimumLevel === 0 &&
      imageryProvider.maximumLevel === 0 &&
      !imageryProvider.filterProvider
    ) {
      imageryProvider._tileWidth = width
      imageryProvider._tileHeight = height
      imageryProvider._tilingScheme = new Cesium.GeographicTilingScheme({
        rectangle,
        numberOfLevelZeroTilesX: rectangle.width / rectangle.height >= 2.0 ? 2 : 1,
        numberOfLevelZeroTilesY: 1,
      })
    } else {
      imageryProvider._tilingScheme = new Cesium.GeographicTilingScheme()
    }
  }

  setCustomImage(customImage0, customImage1, context) {
    const imageryLayer = this._imageryLayer
    const display = imageryLayer.display
    if (Cesium.defined(customImage0)) {
      const customTexture = new WeCustomTextureUniform({
        name: 'customTextureUniform0',
      })
      customTexture.setImage(customImage0, context)
      display.customTextureUniform0 = customTexture
    }
    if (Cesium.defined(customImage1)) {
      customImage1.name = 'customTextureUniform1'
      display.customTextureUniform1 = customImage1
    }
  }

  loadData(frameState) {
    const context = frameState.context
    const imageryLayer = this._imageryLayer

    imageryLayer.loadingRef++
    const { dataRequest } = this

    const urlStamp = this.url + ''
    dataRequest.parser.weContext = context.weContext
    dataRequest
      .loadData()
      .then((images: any) => {
        const display = imageryLayer.display

        if (!images || images.length === 0 || !display || display.isDestroyed()) {
          return
        }
        const image = images[0]
        imageryLayer.updateData(context, images[0], images[1])
        const imageryProvider = this._imageryLayer.imageryProvider
        if (imageryLayer.ready && this._onDataUpdate) {
          this._onDataUpdate(image)
        }

        if (!imageryProvider.ready) {
          this.setupDisplay(image, context)
          this.setupTilingScheme(image)

          Promise.all([
            dataRequest.loadCustom(),
            imageryProvider.filterProvider?.load()]).then(
              (res) => {
                const [customImage0, customImage1] = res[0]
                const hasFilter = res[1] ?? false
                if (!hasFilter) {
                  imageryProvider._filterProvider = null
                }
                this.setCustomImage(customImage0, customImage1, context)
                imageryProvider._ready = true
              },
            )
        }
      })
      .finally(() => {
        imageryLayer.loadingRef--
        if (urlStamp !== this.url + '') {
          this.dirty = true
        }
      })
  }

  update(frameState) {
    const imageryLayer = this._imageryLayer

    if (imageryLayer.loadingRef > 0 || frameState.loadingRef > 0) {
      return
    }

    if (imageryLayer.imageryProvider._ready && !this.dirty) {
      if (Cesium.defined(this.dataRequest.timeSampleArray)) {
        const dataRequest = this.dataRequest
        if (dataRequest.updateTimeData(frameState)) {
          this.dirty = true
        } else if (dataRequest.clampAnime) {
          imageryLayer.display.animeStep = dataRequest.getAnimeStep(frameState)
        }
      }
    }

    if (this.dirty) {
      this.dirty = false
      this.loadData(frameState)
    }
  }

  destroy() {
    return Cesium.destroyObject(this)
  }

  isdestroyed() {
    return false
  }
}

export default WeDataProvider
